home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK2.toast / Development Kits (Disc 2) / QuickDraw GX / Programming Stuff / GX Libraries / DecompressShape.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-09-14  |  14.0 KB  |  459 lines  |  [TEXT/MPS ]

  1.  
  2. /*
  3.     File:        DecompressShape.c
  4.  
  5.     Contains:    graphics libraries - shape decompression
  6.  
  7.     Written by:    Mike Reed
  8.  
  9.     Copyright:    © 1995 by Apple Computer, Inc., all rights reserved.
  10.  
  11.     Writers:
  12.  
  13.         (jtd)    John Daggett
  14.  
  15.     Change History (most recent first):
  16.  
  17.          <2>     9/14/95    jtd        replaced boolean with Boolean
  18.          <1>     9/14/95    jtd        First checked in.
  19. */
  20.  
  21. #include <Drag.h>
  22. #include <Gestalt.h>
  23. #include <ImageCompression.h>
  24. #include <Memory.h>
  25. #include <Scrap.h>
  26.  
  27. #include <GXTypes.h>
  28. #include <GXMath.h>
  29. #include <GXGraphics.h>
  30. #include <GXEnvironment.h>
  31. #include "StorageLibrary.h"
  32. #include "DecompressShape.h"
  33.  
  34. #define LONGALIGN(n)        (((n) + 3) & ~3L)
  35. #define kAtomHeaderSize        (sizeof(Size) + sizeof(OSType))
  36.  
  37. static void RectangleToRect(const gxRectangle* gxr, Rect* qdr)
  38. {
  39.     qdr->left = FixedRound(gxr->left);
  40.     qdr->top = FixedRound(gxr->top);
  41.     qdr->right = FixedRound(gxr->right);
  42.     qdr->bottom = FixedRound(gxr->bottom);
  43. }
  44.  
  45. static long* AppendAtom(long stream[], Size size, OSType tag, const void* data)
  46. {
  47. #ifdef debugging
  48.     if (size & 3)
  49.         DebugStr("\patom size needs to be long aligned");
  50. #endif
  51.  
  52.     *stream++    = size + kAtomHeaderSize;
  53.     *stream++    = tag;
  54.     BlockMove(data, (Ptr)stream, size);
  55.  
  56.     return (long*)((char*)stream + size);
  57. }
  58.  
  59. /*
  60.  *    See the comment on DecompressShape for an explaination of the parameters.
  61.  *    This routine is used by both DecompressShape for embedding shapes in PICTs,
  62.  *    and AddQDGXRecorderFrame for making gx movies.
  63. */
  64. Handle CreateQDGXStream(gxShape source, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground)
  65. {
  66.     #define            gxForPrintingOnlyAtom    'fpto'
  67.     #define            gxEraseBackgroundAtom    'erbg'
  68.  
  69.     long                atomCount, shapeSize, proxieSize, dataSize, fontListSize, eraseSize;
  70.     Handle            dataHdl, shapeHdl;
  71.     gxFlatFontList*        fontList;
  72.     gxTag            fontListTag;
  73.  
  74. #ifdef debugging
  75.     GXIgnoreGraphicsWarning(tags_of_type_flst_removed);
  76. #endif
  77.     shapeHdl = ShapeToHandleWithFlags(source, gxFontListFlatten | gxFontGlyphsFlatten | gxFontVariationsFlatten);
  78. #ifdef debugging
  79.     GXPopGraphicsWarning();
  80. #endif
  81.     if (shapeHdl == nil)
  82.         return nil;
  83.  
  84.     if (proxie)
  85.     {    atomCount = 2;
  86.         proxieSize = LONGALIGN(GetHandleSize((Handle)proxie));
  87.     }
  88.     else
  89.     {    atomCount = 1;
  90.         proxieSize = 0;
  91.     }
  92.     shapeSize = LONGALIGN(GetHandleSize(shapeHdl));
  93.  
  94.     if (forPrintingOnly)
  95.         ++atomCount;
  96.     if (eraseBackground)
  97.         ++atomCount;
  98.  
  99.     fontListSize = 0;
  100.     fontList = nil;
  101.     GXIgnoreGraphicsWarning(count_out_of_range);
  102.     if (GXGetShapeTags(source, gxFlatFontListItemTag, 1, 1, &fontListTag) > 0)
  103.     {    fontListSize = GXGetTag(fontListTag, nil, nil);
  104.         if (fontListSize > 0)
  105.         {    fontList = (gxFlatFontList*)NewPtr(fontListSize);
  106.             if (fontList != nil)
  107.             {    GXGetTag(fontListTag, nil, fontList);
  108.                 fontListSize = LONGALIGN(fontListSize);
  109.                 ++atomCount;
  110.             }
  111.             else
  112.                 fontListSize = 0;
  113.         }
  114.     }
  115.     GXPopGraphicsWarning();        // count_out_of_range
  116.  
  117.     dataSize = atomCount * kAtomHeaderSize + shapeSize + proxieSize + fontListSize + sizeof(long);
  118.     dataHdl = NewHandle(dataSize);
  119.     if (dataHdl == nil)
  120.     {    DisposHandle(shapeHdl);
  121.         if (fontList)
  122.             DisposPtr((Ptr)fontList);
  123.         return nil;
  124.     }
  125.     
  126.     {    long* p = (long*)*dataHdl;
  127.  
  128.         if (forPrintingOnly)
  129.             p = AppendAtom(p, 0, gxForPrintingOnlyAtom, nil);
  130.         if (eraseBackground)
  131.             p = AppendAtom(p, 0, gxEraseBackgroundAtom, nil);
  132.         if (proxie)
  133.             p = AppendAtom(p, proxieSize, 'PICT', *proxie);
  134.         if (fontList)
  135.             p = AppendAtom(p, fontListSize, gxFlatFontListItemTag, fontList);
  136.         p = AppendAtom(p, shapeSize, 'qdgx', *shapeHdl);
  137.         *p++ = 0;        // end of the atom-list
  138.  
  139.         DisposHandle(shapeHdl);
  140.         if (fontList)
  141.             DisposPtr((Ptr)fontList);
  142.     }
  143.     return dataHdl;
  144. }
  145.  
  146. static void GetRidOfAnyQDShapeTags(gxShape shape)
  147. {
  148.     gxShapeType shapeType = GXGetShapeType(shape);
  149.  
  150.     if (shapeType == gxPictureType)
  151.     {    long        index, count;
  152.         gxShape*    subShapes;
  153.     
  154.         count = GXGetPicture(shape, nil, nil, nil, nil);
  155.         if (count > 0)
  156.         {    subShapes = (gxShape*)NewPtr(count * sizeof(gxShape));
  157.             if (subShapes != nil)
  158.             {    GXGetPicture(shape, subShapes, nil, nil, nil);
  159.                 for (index = 0; index < count; index++)
  160.                     GetRidOfAnyQDShapeTags(subShapes[index]);
  161.                 DisposPtr((Ptr)subShapes);
  162.             }
  163.         }
  164.     }
  165.     else if (shapeType == gxRectangleType && GXGetShapeTags(shape, gxQuickDrawPictTag, 1, gxSelectToEnd, nil) > 0)
  166.         GXSetShapeType(shape, gxPictureType);
  167. }
  168.  
  169. /*
  170.  *    This guy returns a Quickdraw picture containing an embedded shape, and a proxie
  171.  *    of the shape, if proxie is not nil. This is called by ShapeToScrap and DragAndDropShape.
  172.  *
  173.  *    theShape            • the shape you want to embedd in a PICT
  174.  *    proxie            • a PICT to be drawn if theShape cannot be drawn (optional but recommended)
  175.  *    forPrintingOnly        • if TRUE, then the decompressor will always look for the proxie
  176.  *                    and theShape will only be used when printing. Use this setting if
  177.  *                    theShape might be too large or too slow when drawn from other apps.
  178.  *                    • If FALSE, then the decompressor will draw theShape unless it
  179.  *                    gets an error, in which case it will look for a proxie.
  180.  *    eraseBackground    • if TRUE, the decompressor will always erase the background to WHITE
  181.  *                    before drawing the shape. This is slower, but needed if the shape does not
  182.  *                    fill its bounding rectangle.
  183.  *                    • if FALSE, the decompressor will just draw the shape. Use this setting
  184.  *                    if the shape entirely fills its bounding rectangle.
  185.  *
  186.  *    The shape [and proxie] is embedded by constructing a stream of atoms. Each atom begins
  187.  *    with a size (long) and a type (OSType) and then the data for that type. After the last atom,
  188.  *    there is a trailing zero (long) to mark the end of the stream. For embedded shapes, the type
  189.  *    is 'qdgx', and for the proxie the type is 'PICT'. Note that the size fields are rounded up to
  190.  *    a multiple of 4. Finally, to alert QuickTime that the data is in this parsable form with a
  191.  *    possible PICT proxie, we add a 'prxy' extension to the ImageDescriptionHandle.
  192.  *
  193.  *    Picture of this form will draw the embedded shape when an application calls DrawPicture
  194.  *    if GX is around, and if not, the proxie will be drawn. When printed, the shape or the proxie
  195.  *    will be printed. This is meant to replace the PicComment described in GX 1.0 for embedding
  196.  *    shapes in pictures.
  197.  *
  198.  *    If you want to include a flatFontList tag, be sure that theShape is a picture, otherwise GX will
  199.  *    not return the tag after GXFlattenShape. The flatFontList tag makes certain printing conditions
  200.  *    more efficient (i.e. font downloading to postscript printers).
  201.  *
  202.  *    Your shape must not contain a gxQuickDrawPictTag, meaning it contains embedded QD data, becuase
  203.  *    this will potentially crash when it tries to print. To fix that, DecompressShape looks for occurrances
  204.  *    of the tag, and converts them to real gx data by calling GXSetShapeType(shape, gxPictureType).
  205. */
  206. PicHandle DecompressShape(gxShape theShape, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground)
  207. {
  208.     #define                kQuickTimeGestalt        'qtim'
  209.  
  210.     PicHandle                thePicture;
  211.     ImageDescriptionHandle    descHdl;
  212.     ImageDescriptionPtr        descPtr;
  213.     Handle                dataHdl;
  214.     long                    version;
  215.  
  216.     if (Gestalt(kQuickTimeGestalt, &version) != noErr)
  217.         return nil;
  218.  
  219.     GetRidOfAnyQDShapeTags(theShape);
  220.  
  221.     /*
  222.      *    Move the shape's topLeft to 0,0 so that it draws neatly inside the picture frame.
  223.      *    Note that the qdgx movie library does not move the shape, since the shape may not
  224.      *    take up the whole frame.
  225.     */
  226.     {    gxRectangle    bounds;
  227.  
  228.         GXGetShapeLocalBounds(theShape, &bounds);
  229.         if (bounds.left || bounds.top)
  230.             GXMoveShape(theShape, -bounds.left, -bounds.top);
  231.         dataHdl = CreateQDGXStream(theShape, proxie, forPrintingOnly, eraseBackground);
  232.         if (bounds.left || bounds.top)
  233.             GXMoveShape(theShape, bounds.left, bounds.top);
  234.     }
  235.     if (dataHdl == nil)
  236.         return nil;
  237.  
  238.     descHdl = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
  239.     if (descHdl)
  240.     {    Rect            shortBounds;
  241.         gxRectangle    bounds;
  242.  
  243.         GXGetShapeLocalBounds(theShape, &bounds);
  244.         RectangleToRect(&bounds, &shortBounds);
  245.         OffsetRect(&shortBounds, -shortBounds.left, -shortBounds.top);    // set the topLeft of the src to 0,0
  246.         thePicture = OpenPicture(&shortBounds);
  247.  
  248.         descPtr = *descHdl;
  249.         descPtr->idSize = sizeof(ImageDescription);
  250.         descPtr->cType = 'qdgx';
  251.         descPtr->vendor = 'appl';
  252.         descPtr->temporalQuality = codecLosslessQuality;
  253.         descPtr->width = shortBounds.right;
  254.         descPtr->height = shortBounds.bottom;
  255.         descPtr->hRes = descPtr->vRes = ff(72);
  256.         descPtr->dataSize = GetHandleSize(dataHdl);
  257.         descPtr->frameCount = 1;
  258.         descPtr->depth = 32;
  259.         descPtr->clutID = -1;
  260.  
  261.         //    If there is a PICT proxie, add an image extension to tell QuickTime, in case GX is not around.
  262.         if (proxie)
  263.         {    Handle prxyVersionHdl = NewHandle(sizeof(long));
  264.  
  265.             if (prxyVersionHdl != nil)
  266.             {    *(long*)*prxyVersionHdl = 0;        // version number for 'prxy' extension
  267.                 SetImageDescriptionExtension(descHdl, prxyVersionHdl, 'prxy');
  268.             }
  269.         }
  270.  
  271.         HLock(dataHdl);
  272.         DecompressImage(*dataHdl, descHdl, ((CGrafPtr)qd.thePort)->portPixMap, &shortBounds, &shortBounds, srcCopy, nil);
  273.         DisposeHandle((Handle)descHdl);
  274.         ClosePicture();
  275.     }
  276.     else
  277.         thePicture = nil;
  278.  
  279.     DisposHandle(dataHdl);
  280.  
  281.     return thePicture;
  282. }
  283.  
  284. /*
  285.  *    This guy returns a Quickdraw picture containing a 1-bit bitmap of the shape.
  286.  *    This is used by ShapeToScrap to create a proxie when calling DecompressShape.
  287.  *    If you want to make the proxie prettier (and larger), change the bitmap to 8-bit.
  288.  *    However, if you're using this in conjunction with DecompressShape to place a
  289.  *    gxShape on the clipboard, 1-bit should be enough, since the actual shape will be
  290.  *    drawn, rather than the proxie (unless forPrintingOnly is true).
  291. */
  292. PicHandle ShapeToPICT(gxShape source)
  293. {
  294.     gxRectangle    bounds;
  295.     gxShape        bitShape;
  296.     gxBitmap        bitmap;
  297.     PicHandle        thePicture;
  298.     Rect            shortBounds;
  299.  
  300.     /*
  301.      *    GetShapeLocalBounds doesn't accurately report the bounds of a gxQuickDrawPictTag.
  302.      *    One option is to convert the tags to real GX pictures.
  303.     */
  304.     GetRidOfAnyQDShapeTags(source);
  305.  
  306.     GXGetShapeLocalBounds(source, &bounds);
  307.     RectangleToRect(&bounds, &shortBounds);
  308.     OffsetRect(&shortBounds, -shortBounds.left, -shortBounds.top);
  309.  
  310.     bitmap.width        = shortBounds.right;
  311.     bitmap.height        = shortBounds.bottom;
  312.     bitmap.rowBytes    = bitmap.width + 31 >> 5 << 2;
  313.     bitmap.pixelSize    = 1;
  314.     bitmap.space        = gxIndexedSpace;
  315.     bitmap.set            = nil;
  316.     bitmap.profile        = nil;
  317.     bitmap.image        = NewPtrClear(bitmap.rowBytes * bitmap.height);
  318.     if (bitmap.image == nil)
  319.         return nil;
  320.  
  321.     bitShape = GXNewBitmap(&bitmap, nil);
  322.     if (bitShape != nil)
  323.     {    gxViewGroup group    = GXNewViewGroup();
  324.         gxViewDevice device    = GXNewViewDevice(group, bitShape);
  325.         gxViewPort port    = GXNewViewPort(group);
  326.         gxTransform trans    = GXCloneTransform(GXGetShapeTransform(source));
  327.  
  328.         GXSetShapeAttributes(source, GXGetShapeAttributes(source) | gxMapTransformShape);
  329.         GXMoveShape(source, -bounds.left, -bounds.top);
  330.         GXSetViewPortDither(port, 4);
  331.         GXSetShapeViewPorts(source, 1, &port);
  332.         GXDrawShape(source);
  333.         GXSetShapeTransform(source, trans);
  334.         GXDisposeTransform(trans);
  335.         
  336.         GXDisposeViewGroup(group);    /* this disposes the gxViewPort and gxViewDevice */
  337.         GXDisposeShape(bitShape);
  338.     }
  339.  
  340.     {    GrafPtr    thePort;
  341.         BitMap    srcBits;
  342.     
  343.         GetPort(&thePort);
  344.         srcBits.baseAddr = bitmap.image;
  345.         srcBits.rowBytes = bitmap.rowBytes;
  346.         srcBits.bounds = shortBounds;
  347.  
  348.         thePicture = OpenPicture(&shortBounds);
  349.         CopyBits(&srcBits, &thePort->portBits, &shortBounds, &shortBounds, srcOr, nil);
  350.         ClosePicture();
  351.     }
  352.     
  353.     DisposPtr((Ptr)bitmap.image);
  354.     
  355.     return thePicture;
  356. }
  357.  
  358. /*
  359.  *    This guy puts a Quickdraw picture on the clipboard containing an embedded shape
  360.  *    and, if addProxie is true, a 1-bit bitmap of the shape. Call this in response to
  361.  *    the user choosing "Copy" or "Cut" from the Edit menu. See comment for DecompressShape
  362.  *    to explain forPrintingOnly.
  363. */
  364. void ShapeToScrap(gxShape source, Boolean addProxie, Boolean forPrintingOnly, Boolean eraseBackground)
  365. {
  366.     PicHandle    picture, proxie;
  367.  
  368.     proxie = addProxie ? ShapeToPICT(source) : nil;
  369.     picture = DecompressShape(source, proxie, forPrintingOnly, eraseBackground);
  370.     if (proxie)
  371.         KillPicture(proxie);
  372.     if (picture)
  373.     {    HLock((Handle)picture);
  374.         ZeroScrap();
  375.         PutScrap(GetHandleSize((Handle)picture), 'PICT', (Ptr)*picture);
  376.         KillPicture(picture);
  377.     }
  378. }
  379.  
  380. /*
  381.  *    The ItemReference is the gxShape to be sent. The dragSendRefCon is ignored.
  382. */
  383. static pascal OSErr LibrarySendDataProc(FlavorType theType, void *dragSendRefCon,
  384.                                 ItemReference theItem, DragReference theDrag)
  385. {
  386.     OSErr    result = noErr;
  387.     gxShape    shape = (gxShape)theItem;
  388.  
  389.     switch (theType) {
  390.     case 'qdgx':
  391.     {    Handle flat = ShapeToHandle(shape);
  392.  
  393.         if (flat)
  394.         {    HLock(flat);
  395.             result = SetDragItemFlavorData(theDrag, theItem, 'qdgx', *flat, GetHandleSize(flat), 0);
  396.             DisposHandle(flat);
  397.         }
  398.         break;
  399.     }
  400.     case 'PICT':
  401.     {    PicHandle proxie = ShapeToPICT(shape);
  402.         PicHandle pict = DecompressShape(shape, proxie, false, true);
  403.  
  404.         if (proxie)
  405.             KillPicture(proxie);
  406.         if (pict)
  407.         {    HLock((Handle)pict);
  408.             result = SetDragItemFlavorData(theDrag, theItem, 'PICT', (Ptr)*pict, GetHandleSize((Handle)pict), 0);
  409.             KillPicture(pict);
  410.         }
  411.         break;
  412.     }
  413.     default:
  414.         result = badDragFlavorErr;
  415.         break;
  416.     }
  417.     return result;
  418. }
  419.  
  420. /*
  421.  *    This guy will take the current mouse-down event and a shape, and handle dragging it to the desktop
  422.  *    or to another Drag-friendly application. It offers to deliver the shape as either 'qdgx' or as a 'PICT'
  423.  *    with the shape embedded using DecompressShape. To offer other flavors, just add additional calls
  424.  *    to AddDragItemFlavor below, and add the corresponding cases in the switch statement in LibrarySendDataProc.
  425. */
  426. void DragAndDropShape(EventRecord* event, gxShape shape)
  427. {
  428.     RgnHandle        hilightRgn;
  429.     gxRectangle    bounds;
  430.     Rect            r;
  431.     DragReference    theDrag;
  432.  
  433.     if (NewDrag(&theDrag) == noErr)
  434.     {    SetDragSendProc(theDrag, LibrarySendDataProc, nil);
  435.         AddDragItemFlavor(theDrag, (unsigned long)shape, 'qdgx', nil, 0, 0);
  436.         AddDragItemFlavor(theDrag, (unsigned long)shape, 'PICT', nil, 0, 0);
  437.         
  438.         hilightRgn = NewRgn();    
  439.         GXGetShapeDeviceBounds(shape, 0, 0, &bounds);
  440.         RectangleToRect(&bounds, &r);
  441.         RectRgn(hilightRgn, &r);
  442.         SetDragItemBounds(theDrag, 1, &r);
  443.  
  444.         // turn the region from a fill into a frame
  445.         {    RgnHandle tempRgn = NewRgn();
  446.  
  447.             CopyRgn(hilightRgn, tempRgn);
  448.             InsetRgn(tempRgn, 1, 1);
  449.             DiffRgn(hilightRgn, tempRgn, hilightRgn);
  450.             DisposeRgn(tempRgn);
  451.         }
  452.         
  453.         TrackDrag(theDrag, event, hilightRgn);
  454.         DisposeDrag(theDrag);
  455.         DisposeRgn(hilightRgn);
  456.     }
  457. }
  458.  
  459.